﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using iNet.Win32;

namespace iNet.Media.Imaging
{
	unsafe class FreeImageBitmap : MediaBase, IBitmapImage
	{
		#region 欄位
		IntPtr _BitmapHandle;
		BitmapSource _BitmapSource;
		readonly double _DpiX;
		readonly double _DpiY;
		readonly int _Height;
		readonly BitmapPalette _Palette;
		readonly IntPtr _PixelBuffer;
		volatile int _PixelBufferLockCounter;
		readonly PixelFormat _PixelFormat;
		readonly IMediaFormat _RawFormat;
		readonly int _Width;
        #endregion
		#region 建構子
		public FreeImageBitmap(MediaStorage mediaStorage, IntPtr bitmapHandle, IMetadata metadata, IMediaFormat rawFormat)
			: base(mediaStorage, true, metadata)
		{
			//初始化欄位
			_BitmapHandle = bitmapHandle;
			_RawFormat = rawFormat;

			//取得影像大小
			_Width = FreeImage.GetWidth(bitmapHandle);
			_Height = FreeImage.GetHeight(bitmapHandle);

			//取得解析度
			_DpiX = (FreeImage.GetDotsPerMeterX(bitmapHandle) / 39.37);
			_DpiY = (FreeImage.GetDotsPerMeterY(bitmapHandle) / 39.37);

			//取得影像格式
			if (!FreeImage.GetFormat(bitmapHandle, out _PixelFormat, out _Palette))
				throw new ArgumentException("Unsupported bitmap format");

			//取得影像內容
			_PixelBuffer = FreeImage.GetBits(bitmapHandle);
			if (_PixelBuffer == IntPtr.Zero)
				throw new ArgumentException("Unsupported bitmap format");

			//反轉像素資料
			FreeImage.FlipVertical(bitmapHandle);
		}
        #endregion
		#region 解構子
		~FreeImageBitmap()
		{
			this.Dispose();
		}
        #endregion
		#region Clone
		public override MediaBase Clone()
		{
			lock (this)
			{
				//確認狀態
				if (this.IsDisposed)
					throw new ObjectDisposedException("FreeImageBitmap");

				//複製像素資料
				int stride = FreeImage.GetStride(_BitmapHandle);
				int bufferSize = (stride * _Height);
				byte[] buffer = new byte[bufferSize];
				Marshal.Copy(_PixelBuffer, buffer, 0, bufferSize);

				//建立點陣圖
				BitmapSource bitmapSource = BitmapSource.Create(_Width, _Height, _DpiX, _DpiY, _PixelFormat, _Palette, buffer, stride);
				return new BitmapImage(this.MediaStorage, bitmapSource, false, this.Metadata, _RawFormat);
			}
		}
		#endregion
		#region GetAsReadOnly
		public override IMedia GetAsReadOnly()
		{
			return this;
		}
		#endregion
		#region CopyPixels
		public void CopyPixels(Int32Rect rect, PixelFormat pixelFormat, BitmapPalette palette, IntPtr buffer, int bufferSize, int stride)
		{
			//確認參數
			if (buffer == IntPtr.Zero)
				throw new ArgumentException();
			if (bufferSize <= 0 || stride <= 0)
				throw new ArgumentOutOfRangeException();

			//複製像素資料
			lock (this)
			{
				//確認狀態
				if (this.IsDisposed)
					throw new ObjectDisposedException("FreeImageBitmap");

				//取得資料
				int nativeStride = FreeImage.GetStride(_BitmapHandle);

				//直接複製資料
				if ((rect.IsEmpty || (rect.X == 0 && rect.Y == 0 && rect.Width == _Width && rect.Height == _Height))
					&& pixelFormat == _PixelFormat
					&& palette == _Palette
					&& stride == nativeStride)
				{
					//確認緩衝區大小
					int dataSize = (stride * _Height);
					if (bufferSize < dataSize)
						throw new ArgumentOutOfRangeException();

					//複製資料
					iNet.Win32.Kernel32.CopyMemory(buffer, _PixelBuffer, dataSize);
					return;
				}

				//裁切點陣圖
				BitmapSource bitmapSource;
				if (rect.IsEmpty || (rect.X == 0 && rect.Y == 0 && rect.Width == _Width && rect.Height == _Height))
					bitmapSource = (this.ToImageSource() as BitmapSource);
				else
					bitmapSource = new CroppedBitmap((this.ToImageSource() as BitmapSource), rect);

				//轉換像素格式
				if (pixelFormat != _PixelFormat || palette != _Palette)
					bitmapSource = new FormatConvertedBitmap(bitmapSource, pixelFormat, palette, 0.5);

				//複製像素資料
				bitmapSource.CopyPixels(Int32Rect.Empty, buffer, bufferSize, stride);
			}
		}
		#endregion
		#region Dispose
		protected override void Dispose(bool disposing)
		{
			lock (this)
			{
				this.UnloadBitmap();
				_BitmapSource = null;
			}
			base.Dispose(disposing);
		}
        #endregion
		#region DpiX
		public double DpiX
		{
			get 
			{
				return _DpiX;
			}
		}
		#endregion
		#region DpiY
		public double DpiY
		{
			get 
			{
				return _DpiY;
			}
		}
		#endregion
		#region HasDirectPixelBuffer
		public bool HasDirectPixelBuffer
		{
			get
			{
				return true;
			}
		}
		#endregion
		#region LockPixelBuffer
		public void LockPixelBuffer()
		{
			lock (this)
			{
				if (this.IsDisposed)
					throw new ObjectDisposedException("FreeImageBitmap");
				++_PixelBufferLockCounter;
			}
		}
		#endregion
		#region Palette
		public BitmapPalette Palette
		{
			get
			{
				return _Palette;
			}
		}
		#endregion
		#region PixelBuffer
		public IntPtr PixelBuffer
		{
			get
			{
				lock (this)
				{
					if (this.IsDisposed)
						throw new ObjectDisposedException("FreeImageBitmap");
					return _PixelBuffer;
				}
			}
		}
		#endregion
		#region PixelBufferStride
		public int PixelBufferStride
		{
			get
			{
				lock (this)
				{
					if (this.IsDisposed)
						throw new ObjectDisposedException("FreeImageBitmap");
					return FreeImage.GetStride(_BitmapHandle);
				}
			}
		}
		#endregion
		#region PixelFormat
		public PixelFormat PixelFormat
		{
			get
			{
				return _PixelFormat;
			}
		}
		#endregion
		#region PixelHeight
		public int PixelHeight
		{
			get
			{
				return _Height;
			}
		}
		#endregion
		#region PixelWidth
		public int PixelWidth
		{
			get
			{
				return _Width;
			}
		}
		#endregion
		#region RawFormat
		public IMediaFormat RawFormat
		{
			get
			{
				return _RawFormat;
			}
		}
		#endregion
		#region ToImageSource
		public ImageSource ToImageSource()
		{
			lock (this)
			{
				//確認狀態
				if (this.IsDisposed)
					throw new ObjectDisposedException("FreeImageBitmap");

				//建立點陣圖
				if (_BitmapSource == null)
				{
					int stride = FreeImage.GetStride(_BitmapHandle);
					int bufferSize = (stride * _Height);
					_BitmapSource = BitmapSource.Create(_Width, _Height, _DpiX, _DpiY, _PixelFormat, _Palette, _PixelBuffer, bufferSize, stride);
					_BitmapSource.Freeze();
				}
				return _BitmapSource;
			}
		}
		#endregion
		#region UnloadBitmap
		void UnloadBitmap()
		{
			if (_BitmapHandle != IntPtr.Zero && _PixelBufferLockCounter <= 0)
			{
				FreeImage.Unload(_BitmapHandle);
				_BitmapHandle = IntPtr.Zero;
			}
		}
		#endregion
		#region UnlockPixelBuffer
		public void UnlockPixelBuffer()
		{
			lock (this)
			{
				if (_PixelBufferLockCounter <= 0)
					return;
				--_PixelBufferLockCounter;
				if (_PixelBufferLockCounter == 0 && this.IsDisposed)
					this.UnloadBitmap();
			}
		}
		#endregion
		#region WritePixels
		public void WritePixels(Int32Rect rect, PixelFormat pixelFormat, BitmapPalette palette, IntPtr buffer, int bufferSize, int stride)
		{
			//確認參數
			if (buffer == IntPtr.Zero)
				throw new ArgumentException();
			if (bufferSize <= 0 || stride <= 0)
				throw new ArgumentOutOfRangeException();

			//寫入像素資料
			lock (this)
			{
				//確認狀態
				if (this.IsDisposed)
					throw new ObjectDisposedException("FreeImageBitmap");

				//取得資料
				int nativeStride = FreeImage.GetStride(_BitmapHandle);

				//直接寫入像素資料
				if ((rect.IsEmpty || (rect.X == 0 && rect.Y == 0 && rect.Width == _Width && rect.Height == _Height))
					&& pixelFormat == _PixelFormat
					&& palette == _Palette
					&& stride == nativeStride)
				{
					//確認緩衝區大小
					int dataSize = (stride * _Height);
					if (bufferSize != dataSize)
						throw new ArgumentOutOfRangeException();

					//複製資料
					iNet.Win32.Kernel32.CopyMemory(_PixelBuffer, buffer, dataSize);
					return;
				}

				//建立緩衝點陣圖
				if (rect.IsEmpty)
					rect = new Int32Rect(0, 0, _Width, _Height);
				int bufferBitmapWidth = rect.Width;
				int bufferBitmapHeight = rect.Height;
				BitmapSource bufferBitmapSource = new WriteableBitmap(bufferBitmapWidth, bufferBitmapHeight, 96, 96, pixelFormat, palette);

				//寫入緩衝區
				(bufferBitmapSource as WriteableBitmap).WritePixels(new Int32Rect(0, 0, bufferBitmapWidth, bufferBitmapHeight), buffer, bufferSize, stride);

				//轉換成原始像素格式
				if (pixelFormat != _PixelFormat || palette != _Palette)
					bufferBitmapSource = new FormatConvertedBitmap(bufferBitmapSource, _PixelFormat, _Palette, 0.5);

				//將資料寫入原始緩衝區
				int bytesPerPixel = (_PixelFormat.BitsPerPixel / 8);
				int bufferBitmapStride = (bytesPerPixel * rect.Width);
				IntPtr bufferPixels = Marshal.AllocHGlobal(bufferBitmapStride * rect.Height);
				try
				{
					int destOffsetInLine = (rect.X * bytesPerPixel);
					byte* destBuffer = ((byte*)_PixelBuffer + (rect.Y * nativeStride) + destOffsetInLine);
					byte* srcBuffer = (byte*)bufferPixels;
					bufferBitmapSource.CopyPixels(Int32Rect.Empty, bufferPixels, (bufferBitmapStride * rect.Height), bufferBitmapStride);
					for (int i = rect.Height; i > 0; --i, srcBuffer += bufferBitmapStride, destBuffer += nativeStride)
						iNet.Win32.Kernel32.CopyMemory(destBuffer, srcBuffer, bufferBitmapStride);
				}
				finally
				{
					Marshal.FreeHGlobal(bufferPixels);
				}
				_BitmapSource = null;
			}
		}
		#endregion
	}
}
